// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:

//===------------ Attributes.h
//
//             This file is part of the reactor project
//                Author: R-Core Team
//
//===---------------------------------------------------------------------===//

#ifndef _RATTRIBUTES_H_
#define _RATTRIBUTES_H_

#include "Any.h"

namespace R {

#define on_attr(__fun, ...)             \
    __fun(NAMES, "names", __VA_ARGS__)  \
    __fun(CLASS, "class", __VA_ARGS__)  \
    __fun(DIM, "dim", __VA_ARGS__)

#define ATTR_VALUES(attr, names, ...)   \
    ATTR_##attr,
enum DefaultAttributes {
// Index of hidden attributes (static attributes)
    on_attr(ATTR_VALUES, 0)
// Number of hidden attributes
    NB_Attributes
};
#undef ATTR_VALUES

	class RAttributes {
		public:
			static Any* checkGetAttr(Any* left_op, Any* attr_name);
			static Any* checkSetAttr(Any* left_op, Any* attr, Any* right_op);
			static Any* checkSetAttributes(Any* left_op, Any* right_op);
			static void initAttributesList();

			static String* getNames(Any* self);
			static void setNames(Any* self, Any* names);
			static void fillNamesWith(Any* self, char* key);
			// we assume that self has an attribute names !
			static bool isInNames(Any* self, char* key);
			static int getIndexByName(Any* self, char* key);
			static char* getNameAt(Any*self, int idx, bool copy = false);

			static void createAndSetAttrById(Any* self, int idx, Any* value);
			static void setAttrById(Any* self, int idx, Any* value);

			// Used for arrays sub[set|script] and so on.
			static Any* getTmpNamesSet(int size);
			static void tmpAddName(char* name);
			static Any* mergeAndSetNames(Any* self, Any* names_to_merge);
	};

	template <typename K, typename V>
	class TmpPrimitiveSet {
		public:
			struct Entry {
					K key;
					V value;
			};

			Entry* tab;
			int capacity;
			int size;
			int tmp_idx;

			void addNewValue(K key, V value) {
				if(tmp_idx != size)
					memmove(tab+tmp_idx+1, tab+tmp_idx, (size-tmp_idx)*sizeof(Entry));
				tab[tmp_idx].key = key;
				tab[tmp_idx].value = value;
				size++;
			}

			bool setKeyIndex(K key) {
				bool found = false;
				int tmp_idx = 0;
				int iend = (size-1)>0 ? size-1 : 0;
				int im = 0;

				if(key > tab[iend].key) {
					tmp_idx = size;
					return false;
				}
				if(key == tab[iend].key) {
					tmp_idx = iend;
					return true;
				}

				while(!found && ((iend - tmp_idx) > 1)){
					im = (tmp_idx + iend)/2;
					found = (tab[im].key == key);
					if(tab[im].key > key) iend = im;
					else tmp_idx = im;
				}

				if(tab[tmp_idx].key == key) return true;
				while(tab[tmp_idx].key > key) tmp_idx--;

				return false;
			}

			void setValue(K key, V value) {
				tab[tmp_idx].value = value;
			}

		public:
			TmpPrimitiveSet(int _size) {
				tab = new Entry[_size];
				tab[0].key = 0;
				tab[0].value = 0;
				capacity = _size;
				size = 0;
			}

			int getSize() { return size; }

			void addValue(K key, V value) {
				int idx= -1;
				Assert(size<=capacity, "TmpPrimitiveSet is full!");
				if(!setKeyIndex(key)) {
					addNewValue(key, value);
				}
			}

			template <class U>
			U* appendTo(U* self) {
					R_PARAM(self);
					R_VAR(String*, names);
					int base_size = 0;
					int j = 0;
					int len = tab[size-1].key;

					TYPE(U) na = getNA<U>();
					base_size = self->length;
					self = self->enlarge(len);

					if((names = RAttributes::getNames(self)) != ConstPool::Null) {
						for(int i = base_size; i <= len; i++) {
							names->content[i] = (char*)"";
							if(tab[j].key == i) {
								self->content[i] = tab[j].value;
								j++;
							} else {
								self->content[i] = na;
							}
						}
					} else {
						for(int i = base_size; i <= len; i++) {
							if(tab[j].key == i) {
								self->content[i] = tab[j].value;
								j++;
							} else {
								self->content[i] = na;
							}
						}
					}

					return self;
			}

			~TmpPrimitiveSet() { delete[] tab; }
	};

	template<typename K>
	class TmpPrimitiveSet<K, Any*> {
			TmpPrimitiveSet() {
				nyi_fatal();
			}
	};

	template <typename K, typename V>
	class TmpPrimitiveSet<K*, V> {
		private:
			V* tab;
			K** index;
			int capacity;
			int size;

			void addNewValue(char* key, V value) {
				tab[size++] = value;
			}

			int getKeyIndex(K* key) {
				for(int i = 0; i < size; i++) {
					if(key == index[i])
							return i;
					// ignore string "" and "NA"
					if(*key == 0 || key == ConstPool::NA_STRING)
						return -1;
				}
				return -1;
			}

		public:
			TmpPrimitiveSet(int _size = 1) {
				tab = new V[_size];
				index = new char*[_size];
				capacity = _size;
				size = 0;
			}

			int getSize() { return size; }

			void addValue(K* key, V value) {
				int idx = -1;
				Assert(size<capacity, "TmpPrimitiveSet is full!");
				if((idx = getKeyIndex(key)) < 0)
					addNewValue(key, value);
				else
					tab[idx] = value;
			}

			template <class U>
			U* appendTo(U* self) {
					R_PARAM(self);
					R_VAR(String*, names);
					int base_size = 0;

					base_size = self->length;
					self = self->enlarge(size);
					memcpy(self->content + base_size, tab, size*sizeof(V));
					names = RAttributes::getNames(self);
					memcpy(names->content + base_size, index, size*sizeof(K*));

					return self;
			}

			~TmpPrimitiveSet() { delete[] tab; delete[] index;}
	};

}

#endif
